package com.dataseek.iot.auth.manager.config.security;


import com.dataseek.iot.core.jwt.JwtTokenAuthenticationFilter;
import com.dataseek.iot.core.jwt.JwtTokenProvider;
import com.dataseek.iot.auth.manager.service.CustomUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;

import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;

import java.net.URI;

@Configuration
@Slf4j
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
                                                JwtTokenProvider tokenProvider,
                                                ReactiveAuthenticationManager reactiveAuthenticationManager) {

        return http.csrf(ServerHttpSecurity.CsrfSpec::disable)
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
                .authenticationManager(reactiveAuthenticationManager)
                .exceptionHandling().authenticationEntryPoint(
                        (swe, e) -> {
                            swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                            System.out.println(e.getMessage());
                            return swe.getResponse()
                                    .writeWith(Mono.just(new DefaultDataBufferFactory().wrap("UNAUTHORIZED OR TOKEN INVALID/EXPIRED".getBytes())));
                        })
                .accessDeniedHandler((swe, e) -> {
                    swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                    return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("FORBIDDEN".getBytes())));
                })
                .and()
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .authorizeExchange(it -> it
                                .pathMatchers("/auth/login", "/login", "/auth/logout", "/logout").permitAll()
                                .pathMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico").permitAll()
                                .pathMatchers("/connection/**").permitAll()
                                .pathMatchers("/swagger-ui/", "/swagger-ui/**", "/webjars/**", "/v3/**", "/swagger-resources/**").permitAll()
                                .anyExchange().authenticated()
                                .and().logout().logoutSuccessHandler((webFilterExchange, authentication) -> {
                                    ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
                                    response.getHeaders().setLocation(URI.create("/auth/logout"));
                                    response.getCookies().remove("Authorization");
                                    log.info("清除权限信息");
                                    return webFilterExchange.getExchange().getSession().flatMap(WebSession::invalidate);
                                })
                )
                .addFilterAt(new JwtTokenAuthenticationFilter(tokenProvider), SecurityWebFiltersOrder.HTTP_BASIC)
                .build();
    }


    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager(CustomUserDetailsService userDetailsService,
                                                                       PasswordEncoder passwordEncoder) {
        UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
        authenticationManager.setPasswordEncoder(passwordEncoder);
        return authenticationManager;
    }
}

